#include <iostream>
#include <string>
#include <unistd.h> 
#include <cstdlib> //等价于<stdlib.h>
#include <vector>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"

using namespace std;

enum
{
    UsageError = 1,
    ArgError = 2,
    PipeError = 3
};

void Usage(const string &proc)
{
    cout << "Usage:" << proc << "subprocess-num" << endl;
}

class Channel //先组织
{
public:
    Channel(int wfd, int sub_id, string& name)
        :_wfd(wfd)
        ,_sub_process(sub_id)
        ,_name(name)
    {}
    void PrintDebug()
    {
        cout << "_wfd: " << _wfd;
        cout << " _sub_process :" << _sub_process;
        cout << " _name: " <<  _name << endl;
    }
    string name() {return _name;} 
    int wfd() {return _wfd;}
    pid_t pid() {return _sub_process;}
    void Close() {close(_wfd);}
    ~Channel()
    {}
private:
    int _wfd;//写端
    pid_t _sub_process;//关联的子进程id
    string _name;//每个通道起名字
};

class ProcessPool //进程池
{
public:
    ProcessPool(int sub_process_num)
        :_sub_process_num(sub_process_num)
    {}
    int CreatProcess(work_t work)//回调函数
    {
        vector<int> fds;

        for(int i = 0;  i < _sub_process_num; ++i)
        {
            int pipefd[2] = {0};//管道
            int n = pipe(pipefd);
            if(n < 0) return PipeError;//创建管道失败

            pid_t id = fork();//创建进程
            if(id == 0)
            {
                if(!fds.empty())    
                {
                    cout << "close w fd: ";
                    for(auto e: fds)
                    {
                        close(e);//把继承父进程的写端关闭掉
                        cout << e << " ";
                    }
                    cout << endl;
                }
                        
                sleep(1);           
                //子进程 - 读
                close(pipefd[1]);
                // 子进程执行任务
                dup2(pipefd[0],0);// 重定向：从标准输入里面读变成从管道里面读取,0位置重定向为管道文件
                work(pipefd[0]);//目前是输出子进程的pid
                exit(0);
            }

            string cname = "channel-" + to_string(i);

            //父进程 - 写
            close(pipefd[0]);

            // 此时wfd传入的是父进程的写端
            _channels.push_back(Channel(pipefd[1],id,cname));

            // 把父进程的wfd保存
            fds.push_back(pipefd[1]);
        }
        return 0;
    }
    int NextChannel()//均衡的选择
    {
        static int next = 0;
        int c =  next;
        next++;
        next %= _channels.size();
        return c;
    }
    void SendTaskCode(int index, uint32_t code)
    {
        cout << "send code:" << code << "to" << _channels[index].name() << "sub process id:" << _channels[index].pid() << endl;  
        write(_channels[index].wfd(),&code,sizeof(code));
    }
    // 所有的子进程退出,只需要关闭所有的w端即可!
    void KillAll()
    {
        for(auto& e : _channels)
        {
            e.Close();
            pid_t pid = e.pid();
            pid_t rid = waitpid(pid, nullptr, 0);
            if(rid == pid)
                cout << "wait sub process " << pid << "success..." << endl;
            cout << e.name() << "close done " << "sub process quit now: " << e.pid() << endl;
        }
    }
    /*
        void Wait()
        {
            for(auto& e : _channels)
            {
                
            }
        }
    */
    
    void Debug()
    {
        for(auto& e : _channels)
            e.PrintDebug();
    }
    ~ProcessPool()
    {} 
private:
    int _sub_process_num;//进程数量
    vector<Channel> _channels;
};

void CtrlProcessPool(ProcessPool* ptr, int cnt)
{
    while(cnt--)
    {
        //a.选择一个进程和通道
        int channel = ptr->NextChannel();
        //sleep(1);
        //cout << channel.name() << endl;
        //b.选择一个任务
        uint32_t code = NextTask();
        //c.发送任务
        ptr->SendTaskCode(channel,code);
        sleep(1);
    }
}

// ./processpool 5(进程的数量)
int main(int argc, char* argv[])
{
    if(argc != 2)//传入参数不符合
    {
        Usage(argv[0]);
        return UsageError;
    }   
    srand((uint64_t)time(nullptr));
    int sub_process_num = stoi(argv[1]);//获取进程的数量

    if(sub_process_num <= 0) return ArgError;//进程数量不符合

    // 1.创建通信信道和子进程
    ProcessPool* ptr = new ProcessPool(sub_process_num);
    ptr->CreatProcess(worker);
    ptr->Debug();

    // 2.控制子进程
    CtrlProcessPool(ptr,10);
    cout << "task run done" << endl;
    //sleep(100);
    
    // 3.回收子进程
    /*
        a.如何让所有的子进程退出
        b.如何让所有已经退出的子进程把他自己进行资源回收wait/waitpid
    */
    // wait sub process
    ptr->KillAll();
    //ptr->Wait();

    delete ptr;
    return 0;
}